using System;
using System.Collections.Generic;
using System.Text;

namespace GPSCore.NMEA
{
    /// <summary>
    /// Rappresent the Type of a NMEA Sentence
    /// </summary>
    public enum NMEASentenceDataType
    { 
        /// <summary>
        /// Fix Data (Time,Position,Elevation)
        /// </summary>
        GPGGA,
        /// <summary>
        /// GPS Satellites in View
        /// </summary>
        GPGSV,
        /// <summary>
        /// GPS DoP and Active Satellites
        /// </summary>
        GPGSA,
        /// <summary>
        /// Actual Track Made Good and Speed Over Ground
        /// </summary>
        //GPVTG,
        /// <summary>
        /// Date and Time
        /// </summary>
        //GPZDA,
        /// <summary>
        /// Recommended Minimum Specific GPS Data
        /// </summary>
        GPRMC,
        /// <summary>
        /// Unknown NMEA Sentence
        /// </summary>
        UNKNOWN
        
    }



    /// <summary>
    /// This is a basic unclassified NMEA Sentence
    /// </summary>
    public class NMEASentenceBase
    {
        #region members

        private bool _isValid = false;

        private string _sentenceString = String.Empty;

        private string[] _splittedWord;

        private int _wordCount;

        private NMEASentenceDataType _sentenceDataType = NMEASentenceDataType.UNKNOWN;

        private bool _isSupported = false;

        private DateTime _timeStamp;

        #endregion

        #region constructors

        /// <summary>
        /// Create a NMEA Sentence Object
        /// </summary>
        /// <param name="Sentence"></param>
        public NMEASentenceBase(string Sentence)
        {
            _sentenceString = Sentence;

            _updateInternalStructures();
        }

        #endregion

        #region public properties

        /// <summary>
        /// Get the local date and time that the packet was created.
        /// </summary>
        public DateTime TimeStamp
        {
            get { return _timeStamp; }
        }

        /// <summary>
        /// Get True if a sentence's checksum matches the calculated checksum.
        /// </summary>
        public bool IsValid
        {
            get { return _isValid; }
        }

        /// <summary>
        /// Get True if the sentence is supported by the engine.
        /// </summary>
        public bool IsSupported
        {
            get { return _isSupported; }
        }

        /// <summary>
        /// Get the sentence splitted into an array of strings rappresenting the "Words".
        /// </summary>
        public string[] Words
        {
            get { return _splittedWord; }
        }

        /// <summary>
        /// Get the number of comma-delimited strings within the sentence.
        /// </summary>
        public int WordsCount
        {
            get { return _wordCount; }        
        }

        /// <summary>
        /// Get the Sentence Data Type 
        /// </summary>
        public NMEASentenceDataType SentenceDataType
        {
            get { return _sentenceDataType; }
        }

       /// <summary>
       /// Get the entire NMEA sentence.
       /// </summary>
        public string Value
        {
            get { return _sentenceString; }
        }

        #endregion

        #region methods

        /// <summary>
        /// Calculates the checksum for a sentence
        /// </summary>
        /// <param name="sentence"></param>
        /// <returns></returns>
        private string _GetChecksum(string sentence)
        {
            // Loop through all chars to get a checksum
            //INSTANT C# NOTE: Commented this declaration since looping variables in 'foreach' loops are declared in the 'foreach' header in C#
            //        char Character = '\0';
            int Checksum = 0;
            foreach (char Character in sentence)
            {
                if (Character == '$')
                {
                    // Ignore the dollar sign
                }
                else if (Character == '*')
                {
                    // Stop processing before the asterisk
                    break;
                }
                else
                {
                    // Is this the first value for the checksum?
                    if (Checksum == 0)
                    {
                        // Yes. Set the checksum to the value
                        Checksum = Convert.ToByte(Character);
                    }
                    else
                    {
                        // No. XOR the checksum with this character's value
                        Checksum = Checksum ^ Convert.ToByte(Character);
                    }
                }
            }
            // Return the checksum formatted as a two-character hexadecimal
            return Checksum.ToString("X2");
        }

        /// <summary>
        /// Update the internal structure basing on 
        /// private members "_sentenceString"
        /// </summary>
        private void _updateInternalStructures()
        {
            //update the Time Stamp
            _timeStamp = DateTime.Now;

            // Compare the characters after the asterisk to the calculation
            if (( (_sentenceString.Length > 0) & ((_sentenceString.IndexOf("*") + 3) != _sentenceString.Length)) | (_sentenceString.IndexOf("*") == -1 ))
            {
                _isValid = false;
            }
            else
            {
                _isValid = _sentenceString.Substring(_sentenceString.IndexOf("*") + 1) == _GetChecksum(_sentenceString);
            }

            //classify the sentence data type
            if (_isValid)
            {
                //split just one time! the sentence in their small peice 
                _splittedWord = _sentenceString.TrimEnd(_sentenceString.Substring(_sentenceString.IndexOf("*")).ToCharArray() ).Split(',');

                //remove from the last splitted word the checksum

                //calculate the lengh....
                _wordCount = _splittedWord.Length;

                //this will contain the DataType withoth the '$' start chart
                string _dataTypeString = _splittedWord[0].TrimStart('$');

                try
                {
                    //cast to a defined Enum Type
                    _sentenceDataType = (NMEASentenceDataType)Enum.Parse(typeof(NMEASentenceDataType), _dataTypeString , true);
                    _isSupported = true;
                }
                catch 
                {
                    _isSupported = false;
                }
            }
            else
            {
                //initialize at least to null the objects
                _splittedWord = null;
            }
        }

        #endregion
    }
}
